
#include <stdio.h>
#include <stdlib.h>

#ifndef offsetof
#define offsetof(st, m) ((char *)&((st *)(0))->m - (char *)((st *)(0)))
#endif

#define upcast(ptr, st, m) ((st *)(((char *)ptr) - offsetof(st, m)))


/* alloc */
void * xmalloc(size_t s) {
    void * result = malloc(s);
    if (result == NULL) {
	fputs("Not enough memory", stderr);
	abort();
    }
    return result;
}

void * xrealloc(void * p, size_t s) {
    void * result = realloc(p, s);
    if (s != 0 && result == NULL) {
	fputs("Not enough memory", stderr);
	abort();
    }
    return result;
}

void xfree(void * p) {
    free(p);
}

/* interfaces */
struct AddServiceVtbl {
    void (* add)(void * self, int num);
    int (* getSum)(void * self);
};

#define AddService_add(self, num) ((self)->vtbl->add(self, num))
#define AddService_getSum(self) ((self)->vtbl->getSum(self))


struct StepServiceVtbl {
    void (* step)(void * self, int step, int amount);
};

struct StepService {
    struct StepServiceVtbl * vtbl;
};

#define StepService_step(self, stepCount, amount) ((self)->vtbl->step(self, stepCount, amount))

/*
 * Foo Impl
 */


struct ServiceImpl {
    /* service vtbl: contain the AddServiceVtbl */
    struct AddServiceVtbl * vtbl;

    /* 2nd impl: step service vtbl */
    struct StepService stepServiceImpl;

    int sum;
};

/* vtbl funcs impl */
static void ServiceImpl_add(void * p, int num) {
    struct ServiceImpl * self = (struct ServiceImpl *) p;
    self->sum = self->sum + num;
}

static int ServiceImpl_getSum(void * p) {
    struct ServiceImpl * self = (struct ServiceImpl *) p;
    return self->sum;
}

static void ServiceImpl_step(void * pi, int step, int amount) {
    struct ServiceImpl * self = (struct ServiceImpl *)((char *)pi - offsetof(struct ServiceImpl, stepServiceImpl));
/* upcast(pi, struct ServiceImpl, stepServiceImpl); */
    self->sum = self->sum + step * amount;
}


static struct AddServiceVtbl ServiceImpl_vtbl = {
    &ServiceImpl_add,
    &ServiceImpl_getSum
};

static struct StepServiceVtbl ServiceImpl_StepService_vtbl = {
    &ServiceImpl_step
};

/* ctor */
struct ServiceImpl * ServiceImpl_new() {
    struct ServiceImpl * result = xmalloc(sizeof(struct ServiceImpl));
    result->vtbl = &ServiceImpl_vtbl;
    result->stepServiceImpl.vtbl = &ServiceImpl_StepService_vtbl;
    result->sum = 0;
    return result;
}




int main() {
    fputs("c-oop sample\n", stdout);

    {
	struct ServiceImpl * s = ServiceImpl_new();
	struct StepService * stepService;
	int r;

	AddService_add(s, 10);
	AddService_add(s, 23);

	r = AddService_getSum(s);
	fprintf(stderr, "(1) r = %d\n", r);

	/* cast to StepService */
	stepService = &s->stepServiceImpl;
	StepService_step(stepService, 10, 2);

	r = AddService_getSum(s);
	fprintf(stderr, "(2) r = %d\n", r);
	
	xfree(s);
    }

    return 0;
}
